package com.tools.commons;

import com.tools.commons.constants.SystemConstants;
import com.tools.commons.model.RequestContext;
import com.tools.commons.model.RequestInfo;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.UUID;

@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE + 9)
public class LogFilter extends OncePerRequestFilter {
    private final ApplicationEventPublisher eventPublisher;

    public LogFilter(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        preHandle(httpServletRequest, httpServletResponse);
        try {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        } finally {
            afterCompletion(httpServletResponse);
        }
    }

    private void preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        RequestInfo requestInfo = new RequestInfo();
        RequestContext.setRequestInfo(requestInfo);
        String requestId = httpServletRequest.getHeader(SystemConstants.REQUEST_ID);
        requestId = (requestId == null) ? UUID.randomUUID().toString().replace("-", "") : requestId;
        requestInfo.setRespStatus(HttpStatus.OK.value());
        requestInfo.setRequestId(requestId);
        requestInfo.setHost(httpServletRequest.getHeader(HttpHeaders.HOST));
        requestInfo.setIp(httpServletRequest.getRemoteAddr());
        requestInfo.setMethod(httpServletRequest.getMethod());
        requestInfo.setPath(httpServletRequest.getQueryString());
        requestInfo.setReferer(httpServletRequest.getHeader(HttpHeaders.REFERER));
        requestInfo.setReqContentLength(httpServletRequest.getHeader(HttpHeaders.CONTENT_LENGTH));
        requestInfo.setService(httpServletRequest.getRequestURI());
        requestInfo.setUserAgent(httpServletRequest.getHeader(HttpHeaders.USER_AGENT));
        requestInfo.setXForwardedFor(httpServletRequest.getHeader(SystemConstants.X_FORWARDED_FOR));
        requestInfo.setRespTime(System.nanoTime());
        HttpHeaders headers = new HttpHeaders();
        for (Enumeration<?> names = httpServletRequest.getHeaderNames();
             names.hasMoreElements(); ) {
            String name = (String) names.nextElement();
            for (Enumeration<?> values = httpServletRequest.getHeaders(name);
                 values.hasMoreElements(); ) {
                headers.add(name, (String) values.nextElement());
            }
        }
        requestInfo.setUserData(headers.toString());
        MDC.put(SystemConstants.REQUEST_ID, requestId);
        httpServletResponse.setHeader(SystemConstants.REQUEST_ID, requestId);
    }

    private void afterCompletion(HttpServletResponse httpServletResponse) {
        RequestInfo requestInfo = RequestContext.getRequestInfo();
        requestInfo.setRespContentLength(0);
        requestInfo.setRespContentType(httpServletResponse.getContentType());
        long tm = System.nanoTime() - requestInfo.getRespTime();
        requestInfo.setRespTime(tm);
        requestInfo.setRespStatus(httpServletResponse.getStatus());
        eventPublisher.publishEvent(new LogEvent(this, requestInfo));
    }
}
